#ifndef __GLF_MATH_FIXED_H_INCLUDED__
#define __GLF_MATH_FIXED_H_INCLUDED__

#include <glf/config.h>
#include <glf/core/primitives.h>

//TODO implement fixed<T,SHIFT> class

namespace glf {

template<typename T, int SHIFT>
inline T tofixed(T val)
{
	return val << SHIFT;
}

template<typename T, int SHIFT>
inline float tofloat(T fx)
{
	return fx / float( 1 << fx);
}

template<typename T, int SHIFT>
inline T fromfloat(float f)
{
	return T(f * (1 << SHIFT));
}

template<>
inline float fromfloat<float,1>(float f)
{
	return f;
}

template<typename T, int SHIFT>
inline T mult(T v1, T v2)
{
	T result(((int64)v1 * v2) >> SHIFT);

	return result;
}

template<typename T, int SHIFT>
inline T div(T v1, T v2)
{
	T result(((int64)v1 << SHIFT) / v2);

	return result;
}

template<>
inline float mult<float,1>(float v1, float v2)
{
	return v1 * v2;
}

template<>
inline float div<float,1>(float v1, float v2)
{
	return v1 / v2;
}

template<typename T,int SHIFT>
T ftofix(float x)
{
	return T(x * (1<<SHIFT));
}

template<typename T,int SHIFT>
T ftofix(double x)
{
	return T(x * (1<<SHIFT));
}


template<typename T, int SHIFT>
struct fixed {
	fixed(const fixed& x) : v(x.v){}
	fixed() : v() {}
	explicit fixed(T i) : v(i) {}
	explicit fixed(float f) : v(f * (1<<SHIFT)) {}

	T v;
};

template<typename T,int SHIFT>
class Fixed : public fixed<T,SHIFT>
{
	//T v;
public:
	Fixed();
	Fixed(const Fixed &x);
	Fixed(const fixed<T,SHIFT>& x)
	{
		this->v = x.v;
	}

	Fixed(int x);
	Fixed(float x);
	Fixed(double x);

	Fixed(int num, int denum);

	//operator int() const;
	//operator float() const;
	//operator double() const;

	Fixed& operator = (const Fixed& x);
	Fixed& operator = (int x);
	Fixed& operator = (float x);
	Fixed& operator = (double x);

	Fixed& operator +=  (const Fixed& x);
	Fixed& operator +=  (int x);
	Fixed& operator +=  (float x);
	Fixed& operator +=  (double x);

	Fixed& operator -=  (const Fixed& x);
	Fixed& operator -=  (int x);
	Fixed& operator -=  (float x);
	Fixed& operator -=  (double x);

	Fixed& operator *=  (const Fixed& x);
	Fixed& operator *=  (int x);
	Fixed& operator *=  (float x);
	Fixed& operator *=  (double x);

	Fixed& operator /=  (const Fixed& x);
	Fixed& operator /=  (int x);
	Fixed& operator /=  (float x);
	Fixed& operator /=  (double x);

	Fixed& operator <<= (int x);
	Fixed& operator >>= (int x);

	friend Fixed<T,SHIFT> operator*(const Fixed& a, const Fixed& b)
	{
		return fixed<T,SHIFT>(mult<T,SHIFT>(a.v, b.v));
	}

	friend Fixed operator/(const Fixed& a, const Fixed& b)
	{
		return fixed<T,SHIFT>(div<T,SHIFT>(a.v, b.v));
	}

	Fixed& operator ++ ();
	Fixed& operator -- ();

	Fixed operator ++ (int);
	Fixed operator -- (int);

	Fixed operator - () const;
};

//template<typename T, int SHIFT>
//Fixed<T,SHIFT> operator*(float x, Fixed<T,SHIFT>& f)
//{
//	return f * x;
//}

template<typename T, int SHIFT>
Fixed<T,SHIFT>::Fixed() : 
	fixed<T,SHIFT>()
{

}

template<typename T, int SHIFT>
Fixed<T,SHIFT>::Fixed(const Fixed &x) :
	fixed<T,SHIFT>(x.v)
{

}

template<typename T, int SHIFT>
Fixed<T,SHIFT>::Fixed(int x) 
	//: fixed<T,SHIFT>(x) //tofixed<T,SHIFT>(x))
{
	this->v = tofixed<T,SHIFT>(x);
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>::Fixed(int num, int denum)
{
	this->v = (tofixed<T,SHIFT>(num) / denum);
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>::Fixed(float x) :
	fixed<T,SHIFT>(ftofix<T,SHIFT>(x))
{

}

template<typename T, int SHIFT>
Fixed<T,SHIFT>::Fixed(double x) :
	fixed<T,SHIFT>(ftofix<T,SHIFT>(x))
{

}

/*
template<typename T, int SHIFT>
Fixed<T,SHIFT>::operator int() const
{
	 return fixtoi(v);
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>::operator float() const
{
	 return fixtof(v);
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>::operator double() const
{
	 return fixtof(v);
}
*/

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator = (const Fixed &x)
{
	 this->v = x.v;
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator = (int x)
{
	 this->v = tofixed<T,SHIFT>(x);	  
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator = (float x)
{
	 this->v = ftofix<T,SHIFT>(x);	  
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator = (double x)
{
	 this->v = ftofix<T,SHIFT>(x);	  
	 return *this;
}


template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator +=  (const Fixed& x)
{
	 this->v += x.v;			 
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator +=  (int x)
{
	 this->v += tofixed<T,SHIFT>(x);	 
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator +=  (float x)
{
	 this->v += ftofix<T,SHIFT>(x);	 
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator +=  (double x)
{
	 this->v += ftofix<T,SHIFT>(x);	 
	 return *this;
}


template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator -=  (const Fixed& x)
{
	 this->v -= x.v;			 
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator -=  (int x)
{
	 this->v -= tofixed<T,SHIFT>(x);	 
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator -=  (float x)
{
	 this->v -= ftofix<T,SHIFT>(x);	 
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator -=  (double x)
{
	 this->v -= ftofix<T,SHIFT>(x);	 
	 return *this;
}


template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator *=  (const Fixed& x)
{
	 this->v = mult<T,SHIFT>(this->v, x.v);			
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator *=  (int x)
{
	 this->v *= x;							
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator *=  (float x)
{
	 this->v = ftofix<T,SHIFT>(tofloat<T,SHIFT>(this->v) * x);  
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator *=  (double x)
{
	 this->v = ftofix<T,SHIFT>(tofloat<T,SHIFT>(this->v) * x);  
	 return *this;
}


template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator /=  (const Fixed& x)
{
	 this->v = div<T,SHIFT>(this->v, x.v);			
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator /=  (int x)
{
	 this->v /= x;							
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator /=  (float x)
{
	 this->v = ftofix(fixtof(this->v) / x);  
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator /=  (double x)
{
	 this->v = ftofix(fixtof(this->v) / x);
	 return *this;
}


template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator <<= (int x)
{
	 this->v <<= x;
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator >>= (int x)
{
	 this->v >>= x;
	 return *this;
}


template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator ++ ()
{
	 this->v += tofixed<T,SHIFT>(1);
	 return *this;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT>& Fixed<T,SHIFT>::operator -- ()
{
	 this->v -= tofixed<T,SHIFT>(1);	 
	 return *this;
}


template<typename T, int SHIFT>
Fixed<T,SHIFT> Fixed<T,SHIFT>::operator ++ (int)
{
	 Fixed t;  
	 t.v = this->v;	
	 this->v += tofixed<T,SHIFT>(1);  
	 return t;
}

template<typename T, int SHIFT>
Fixed<T,SHIFT> Fixed<T,SHIFT>::operator -- (int)
{
	 Fixed t;  
	 t.v = this->v;	
	 this->v -= tofixed<T,SHIFT>(1);  
	 return t;
}


template<typename T, int SHIFT>
Fixed<T,SHIFT> Fixed<T,SHIFT>::operator - () const
{
	 Fixed t;  
	 t.v = -this->v;
	 return t;
}

typedef Fixed<int,16> fx16;

}//namespace glf

#endif
